/*
 * Copyright (C) 2005 Dmitri Maximovich <maxim@md.pp.ru>
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

// Project object
function Project() {
	this.name = "";
	this.url = "";
	this.passed = false;
	this.lastRun;
	this.lastSuccess;
	// only for dotnet projects
	this.activity;

	this.toString = function() {
		return "Project[name="+this.name+", url="+this.url+", passed="+this.passed+", lastRun="+this.lastRun
		+", lastSuccess="+this.lastSuccess+", activity="+this.activity+"]";
	}
}

// CC server object
function CCm(aText, prefs) {
	this.generated = "";
	this.projects = new Array();
	// derived attributes
	this.allPassed = true;
	this.maxNameLen = 0;
	// type: "N"=dotnet, "J"=java, "R"= RSS (cc 2.4.1)
	this.type;
	// when this object was created
	this.created = new Date();

	this.toString = function() {
		var result = "Cc[type="+this.type+", generated="+this.generated+", allPassed="+this.allPassed+", maxNameLen="+this.maxNameLen;

		for(i = 0; i < this.projects.length; i++) {
			var project = cc.projects[i];
			result += "\n\t"+project.toString();
		}
		result += "\n]";
		return result;
	}

	
	// all adds to projects array should go through this function
	this.addProject = function(aProject) {
		this.projects.push(aProject);
		if (!aProject.passed) {
			this.allPassed = false;
		}
		if (aProject.name.length > this.maxNameLen) {
			this.maxNameLen = aProject.name.length;
		}
	}

	this.parseDotNetHtml = function(aText, prefs) {
		this.generated = "This is .NET project";
		
		// position to the beginning of the table data
		var index = aText.indexOf('id="StatusGrid"');
		aText = aText.substring(index, aText.length);
		index = aText.indexOf('</tr>');
		aText = aText.substring(index, aText.length);
		
		var projectMarker = 'default.aspx?_action_ViewProjectReport=true';
		while(aText.indexOf(projectMarker) > 0) {
			project = new Project();
			
			// find the project
			index = aText.indexOf(projectMarker);
			aText = aText.substring(index, aText.length);
			index = aText.indexOf('"');
			project.url = aText.substring(0, index);

			project.name=project.url.substring(project.url.indexOf('&project=')+9,project.url.length);

			aText=aText.substring(index,aText.length);

			// skip if not included/excluded
			if (!isInclude(project.name, prefs.include, prefs.exclude)) {
				continue;
			}

			// get the status
			index = aText.indexOf("<b>");
			aText=aText.substring(index+3, aText.length);
			index = aText.indexOf("</b>");
			if (aText.substring(0, index) == "Success") {
				project.passed = true;
			}

			// skip if include only failed is set and project isn't failed
			if (prefs.onlyFailed && project.passed) {
				continue;
			}

			// get the last build date
			index = aText.indexOf('<font color="Black">');
			aText = aText.substring(index+20, aText.length);
			index = aText.indexOf('</font>');
			project.lastRun = aText.substring(0,index);

			// skip last build label
			index = aText.indexOf('<font color="Black">');
			aText = aText.substring(index+20, aText.length);
			// skip CCNet status
			index = aText.indexOf('<font color="Black">');
			aText = aText.substring(index+20, aText.length);

			// get activity
			index = aText.indexOf('<font color="Black">');
			aText = aText.substring(index+20, aText.length);
			index = aText.indexOf('</font>');
			project.activity = aText.substring(0,index);
			
			project.lastSuccess = "unknown";

			// add tp projects
			this.addProject(project);
		}
	}

	this.parseJavaHtml = function(aText, prefs) {
		//get listing 
		var find='listing generated at';
		var index=aText.indexOf(find);
		aText=aText.substring(index+find.length,aText.length);
		index=aText.indexOf('<');
		this.generated = aText.substring(0,index);
			
		while(aText.indexOf("buildresults")>0){
			project=new Project();
			
			//first find the project
			index=aText.indexOf("buildresults");
			aText=aText.substring(index,aText.length);
			index=aText.indexOf('"');
			project.url=aText.substring(0,index);
			project.name=project.url.substring(project.url.indexOf('/')+1,project.url.length);
			aText=aText.substring(index,aText.length);			

			// skip if not included/excluded
			if (!isInclude(project.name, prefs.include, prefs.exclude)) {
				continue;
			}
			
			//now get the status
			var ignore='</a></td><td class=';
			index=aText.indexOf(ignore);
			aText=aText.substring(index+ignore.length,aText.length);
			index=aText.indexOf('>');
			aText=aText.substring(index+1,aText.length);
			ignore='</td><td align="center">';
			index=aText.indexOf(ignore);
			if (aText.substring(0,aText.indexOf(ignore)) == "passed") {
				project.passed=true;
			}

			// skip if include only failed is set and project isn't failed
			if (prefs.onlyFailed && project.passed) {
				continue;
			}
			
			//get the last build date
			aText=aText.substring(index+ignore.length,aText.length);
			index=aText.indexOf("<");
			project.lastRun = aText.substring(0,index);
	
			//get the last successful run time
			index='</td><td align="center">';
			aText=aText.substring(index,aText.length);	
			index=aText.indexOf('<');
			project.lastSuccess = aText.substring(0,index);
	
			// add to projects
			this.addProject(project);
		}	
	}

    this.parseRss = function(aText, prefs)
    {
        while (aText.indexOf("<item>") >= 0)
        {
            var nextItem = aText.substring(aText.indexOf("<item>")+6, aText.indexOf("</item>"));
            var nextTitle = nextItem.substring(nextItem.indexOf("<title>")+7, nextItem.indexOf("</title>"));
            var nextName = nextTitle.substring(0, nextTitle.indexOf(" "));
            nextTitle = nextTitle.substring(nextTitle.indexOf(" ")+1, nextTitle.length);
            var nextResult = nextTitle.substring(0, nextTitle.indexOf(" "));
            nextTitle = nextTitle.substring(nextTitle.indexOf(" ")+1, nextTitle.length);
            var nextTime = nextTitle;
            var nextUrl = nextItem.substring(nextItem.indexOf("<link>")+6, nextItem.indexOf("</link>"));

            if (isInclude(nextName, prefs.include, prefs.exclude) && !(prefs.onlyFailed && (nextResult == "passed")))
            {
                project = new Project();
                project.url = nextUrl;
                project.name = nextName;
                if (nextResult == "passed")
                {
                    project.passed = true;
                }
                project.lastRun = nextTime;
                project.lastSuccess = 0;

                this.addProject(project);
            }

            aText = aText.substring(aText.indexOf("</item>")+7, aText.length);
        }
    }

	// sort function to sort by name
	// parameters are type of Project
	this.sortByName = function(a, b) {
		if (a.name < b.name) {
			return -1;
		}
		else if (a.name > b.name) {
			return 1;
		}
		return 0;
	}

	// sort function to sort by date
	// parameters are type of Project
	this.sortByDate = function(a, b) {
		var da;
		var db;
		var dateFormat = this.prefs.dateFormat;
		
		if (dateFormat && dateFormat != "") {
			// convert to date
			da = getDateFromFormat(a.lastRun, dateFormat);
			db = getDateFromFormat(b.lastRun, dateFormat);
			if (da == 0 || db == 0) {
				ccLog("Error parsing date using format: "+dateFormat);
			}
		}
		else {
			da = a.lastRun;
			db = b.lastRun;
		}
		// java.lang.System.err.println("da= "+da+", db="+db);
		// compare
		if (da < db) {
			return 1;
		}
		else if (da > db) {
			return -1;
		}
		return 0;
	}

	// constructor code
	// replace line breaks, spaces etc
	aText = compressWhiteSpace(aText);
	this.type = getType(aText);
	if (this.type == "J") {
		this.parseJavaHtml(aText, prefs);
	}
	else if (this.type == "R") {
        this.parseRss(aText, prefs);
    } 	
	else {
		this.parseDotNetHtml(aText, prefs);
	}
	
//	java.lang.System.err.println(this.projects.length);
	// sort
	if (prefs.sortBy == 1) {
		this.projects.sort(this.sortByName);
	}
	else if (prefs.sortBy == 2) {
		this.projects.sort(this.sortByDate);
	}
}

function compressWhiteSpace(s) {
  // Condense white space.
  s = s.replace(/\s+/g, " ");
  s = s.replace(/^\s(.*)/, "$1");
  s = s.replace(/(.*)\s$/, "$1");

  // Remove uneccessary white space around operators, braces and parentices.
  s = s.replace(/\s([\x21\x25\x26\x28\x29\x2a\x2b\x2c\x2d\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5d\x5c\x7b\x7c\x7d\x7e])/g, "$1");
  s = s.replace(/([\x21\x25\x26\x28\x29\x2a\x2b\x2c\x2d\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5d\x5c\x7b\x7c\x7d\x7e])\s/g, "$1");
  return s;
}

// return type of the server e.g. Java/DonNet
function getType(aText) {
	if (aText.indexOf(".aspx?") > 0) {
		return "N";
	}
    else if (aText.indexOf("/rss") > 0) {
        return "R";
    } 	
	return "J";
}

// determine if given project name should be included or excluded from results
// TODO: optimize to parse list just once
function isInclude(aName, include, exclude) {
	var result = _included(include, aName) && !_excluded(exclude, aName);
	//ccLog("isInclude(): "+aName+" -> "+result);
	return result;
}

function _included(pattern, val) {
	var p = pattern.split(",");
    var matchTested = false;
    var matched = false;
    
    for (i = 0; i < p.length; i++) {
        if (p[i].length == 0) {
            continue;
        }
    
        matchTested = true;
        if(val.match(p[i])) {
            matched = true;
        }
    }
    if (matchTested && !matched) {
        return false;
    }
    return true;
}

function _excluded(pattern, val) {
	var p = pattern.split(",");
    var matchTested = false;
    var matched = false;
    
    for (i = 0; i < p.length; i++) {
        if (p[i].length == 0) {
            continue;
        }
    
        matchTested = true;
        if(val.match(p[i])) {
            matched = true;
        }
    }
    if (matchTested && matched) {
        return true;
    }
    return false;
}

